package com.senseauto.mediacodecdemo;

import android.content.Context;
import android.content.res.Configuration;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.Vector;

/**
 * 为了防止编码耗时，特意改成了线程的方式，图像数据线放队列中
 */
//换成线程的方式，图像数据先放到线程的缓存队列，以防止onImageAvailable编码忙不过来而卡主了界面预览
public class YuvEncoderH264Thread extends Thread{
    String TAG = "YuvEncoderH264Thread";
    private int width, height;
    private int frameRate = 30;
    private MediaCodec mediaCodec;
    private int type = TYPE_LANDSCAPE;
    public static int TYPE_LANDSCAPE = 1;
    public static int TYPE_PORTRAIT = 2;
    private boolean isRecording = false;//是否在录制
    private int MAX_COUNT = 20;
    private String videoSavePath;
    private FileOutputStream fos;

    boolean flagSave = false;//保存yuv，调试看画面
    long frameCount = 0; //第10帧或者第20针保存yuv图像

    private Context mContext;

    private LinkedList<FrameData> vector = new LinkedList<FrameData>();//缓冲图像队列

    public YuvEncoderH264Thread(Context context, int width, int height) {
        this.width = width;
        this.height = height;
        //平板横屏输出的是1920*1440，有点像正方形的尺寸
        Log.e(TAG, "YuvEncoderH264Thread 输入的编码MediaCodec尺寸：width=" + width + " height=" + height);
        this.mContext = context;
        setScreenOrientationInfo();
        initMediaCodec();
    }

    public void setVideoSavePath(String videoSavePath) {
        this.videoSavePath = videoSavePath;
        try {
            fos = new FileOutputStream(new File(videoSavePath));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void setScreenOrientationInfo() {

        Configuration configuration = mContext.getResources().getConfiguration();
        if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Log.e("NV21EncoderH264", "configuration : 横屏");
            type = TYPE_LANDSCAPE;
        } else {
            Log.e("NV21EncoderH264", "configuration : 竖屏屏");
            type = TYPE_PORTRAIT;
        }
    }

    private void initMediaCodec() {
        try {
            mediaCodec = MediaCodec.createEncoderByType("video/avc");
            //height和width一般都是照相机的height和width。
            MediaFormat mediaFormat = null;
            if (type == TYPE_LANDSCAPE) {//横屏
                mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
            } else {//竖屏有旋转的操作
                //TODO 因为获取到的视频帧数据是逆时针旋转了90度的，所以这里宽高需要对调
                mediaFormat = MediaFormat.createVideoFormat("video/avc", height, width);
            }

            //描述平均位速率（以位/秒为单位）的键。 关联的值是一个整数
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5);
            //描述视频格式的帧速率（以帧/秒为单位）的键。帧率，一般在15至30之内，太小容易造成视频卡顿。
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
            //色彩格式，具体查看相关API，不同设备支持的色彩格式不尽相同
            //mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            //关键帧 I帧 间隔时间，单位是秒
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
            mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            //开始编码
            mediaCodec.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void encoderH264(byte[] data) {
        try {
            //为了防止撑爆内存，保证换成队列只有20个图像数据
            if(vector.size()>=MAX_COUNT){
                vector.remove(0);
            }
            vector.add(new FrameData(data));
        } catch (Exception t) {
            t.printStackTrace();
        }
    }

    public void run(){
        while (isRecording) {
            try {
                if(vector.size()>0) { //等把数据加到缓存队列后才开始工作
                    //拿到输入缓冲区,用于传送数据进行编码
                    ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
                    //拿到输出缓冲区,用于取到编码后的数据
                    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

                    //mediaCodec.dequeueInputBuffer会阻塞
                    int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);

                    //当输入缓冲区有效时,就是>=0
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                        inputBuffer.clear();

                        FrameData frameData = vector.remove(0);//移除第一个数据
                        //往输入缓冲区写入数据
                        inputBuffer.put(frameData.getData());
                        //五个参数，第一个是输入缓冲区的索引，第二个数据是输入缓冲区起始索引，第三个是放入的数据大小，第四个是时间戳，保证递增就是
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, frameData.data.length, System.nanoTime() / 1000, 0);
                    }
                    //测试时发现车机DSP芯片的编码队列就4个，一直空不出来新的，卡在这里了mediaCodec.dequeueOutputBuffer
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    //拿到输出缓冲区的索引
                    int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
                    Thread.sleep(20);//等芯片编码完数据
                    while (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
                        byte[] outData = new byte[bufferInfo.size];
                        outputBuffer.get(outData);

                        //outData就是输出的h264数据
                        if (fos != null) {
                            fos.write(outData);
                        }
                        mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
                    }
                } else {
                    //等数据添加
                    Thread.sleep(20);
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "mediaCodec编码报错了！"+e.getMessage());
            }
        }
    }

    public void startRecord(){
        isRecording = true;
        start();//启动线程
    }

    public void stopRecord(){
        isRecording = false;
        vector.clear();
        if(fos!=null){
            try {
                fos.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public boolean isRecording(){
        return isRecording;
    }
}
